import EventEmitter from "event-emitter";

import Hook from "../utils/hook.js";
import Chunker from "../chunker/chunker.js";
import Polisher from "../polisher/polisher.js";

import { initializeHandlers, registerHandlers } from "../utils/handlers.js";

class Previewer {
	/**
	 *
	 * @param {*} options 配置
	 */
	constructor(options) {
		// this.preview = this.getParams("preview") !== "false";

		this.settings = options || {};

		// 处理样式
		this.polisher = new Polisher(false);

		// 分割内容
		this.chunker = new Chunker(undefined, undefined, this.settings);

		// 钩子
		this.hooks = {};
		this.hooks.beforePreview = new Hook(this);
		this.hooks.afterPreview = new Hook(this);

		// 默认值
		this.size = {
			width: {
				value: 8.5,
				unit: "in",
			},
			height: {
				value: 11,
				unit: "in",
			},
			format: undefined,
			// 方向
			orientation: undefined,
		};

		this.chunker.on("page", (page) => {
			this.emit("page", page);
		});

		this.chunker.on("rendering", () => {
			this.emit("rendering", this.chunker);
		});
	}

	/**
	 * 预览
	 * @param { DocumentFragment} content  DocumentFragment 文档片段
	 * @param {*} stylesheets 样式数组
	 * @param {HTMLElement} renderTo 渲染位置 默认为body
	 * @returns {Promise<Chunker>} 返回Chunker
	 */
	async preview(content, stylesheets, renderTo) {
		// 触发beforePreview钩子执行
		await this.hooks.beforePreview.trigger(content, renderTo);

		if (!content) {
			content = this.wrapContent();
		}

		if (!stylesheets) {
			stylesheets = this.removeStyles();
		}

		// 引导初始化 为document插入初始化style标签样式
		this.polisher.setup();

		this.handlers = this.initializeHandlers();

		// 转化后的样式插入document
		await this.polisher.add(...stylesheets);

		// 方法返回一个精确到毫秒的 DOMHighResTimeStamp,用来对chunker进行性能分析
		let startTime = window.performance.now();

		// Render flow 渲染流程
		let flow = await this.chunker.flow(content, renderTo);

		let endTime = window.performance.now();

		flow.performance = endTime - startTime;
		flow.size = this.size;

		this.emit("rendered", flow);

		await this.hooks.afterPreview.trigger(flow.pages);

		return flow;
	}

	/**
	 * 用template包裹内容，返回content DocumentFragment
	 * @returns {DocumentFragment} DocumentFragment
	 */
	wrapContent() {
		// 用template标签包裹body
		let body = document.querySelector("body");

		// 检查是否有template标签
		let template = body.querySelector(
			":scope > template[data-ref='pagedjs-content']"
		);

		if (!template) {
			// 否则创建一个
			template = document.createElement("template");
			template.dataset.ref = "pagedjs-content";
			template.innerHTML = body.innerHTML;
			body.innerHTML = "";
			body.appendChild(template);
		}

		return template.content;
	}

	/**
	 * 移除document已有的样式，转换已删除的样式放到一个数组中并返回
	 *
	 * @param {Document} doc document
	 * @returns {Array} 数组
	 */
	removeStyles(doc = document) {
		// 获取所有样式表
		const stylesheets = Array.from(
			doc.querySelectorAll(
				"link[rel='stylesheet']:not([data-pagedjs-ignore], [media~='screen'])"
			)
		);
		// 获取内联样式
		const inlineStyles = Array.from(
			doc.querySelectorAll(
				"style:not([data-pagedjs-inserted-styles], [data-pagedjs-ignore], [media~='screen'])"
			)
		);
		const elements = [...stylesheets, ...inlineStyles];
		return (
			elements
				// 保持顺序
				.sort(function (element1, element2) {
					//可以比较当前节点与任意文档中的另一个节点的位置关系
					const position = element1.compareDocumentPosition(element2);
					if (position === Node.DOCUMENT_POSITION_PRECEDING) {
						// otherNode 在 node 之前
						return 1;
					} else if (position === Node.DOCUMENT_POSITION_FOLLOWING) {
						// otherNode 在 node 之后
						return -1;
					}
					return 0;
				})
				// 提取href
				.map((element) => {
					if (element.nodeName === "STYLE") {
						const obj = {};
						obj[window.location.href] = element.textContent;
						element.remove();
						return obj;
					}
					if (element.nodeName === "LINK") {
						element.remove();
						return element.href;
					}
					// ignore
					console.warn(`Unable to process: ${element}, ignoring.`);
				})
		);
	}

	/**
	 * 初始化处理程序
	 * @returns {Array} 返回handlers
	 */
	initializeHandlers() {
		let handlers = initializeHandlers(this.chunker, this.polisher, this);

		handlers.on("size", (size) => {
			this.size = size;
			this.emit("size", size);
		});

		handlers.on("atpages", (pages) => {
			this.atpages = pages;
			this.emit("atpages", pages);
		});

		return handlers;
	}

	/**
	 * 注册处理程序
	 * @returns {undefined}
	 */
	registerHandlers() {
		return registerHandlers.apply(registerHandlers, arguments);
	}

	getParams(name) {
		let param;
		let url = new URL(window.location);
		let params = new URLSearchParams(url.search);
		for (var pair of params.entries()) {
			if (pair[0] === name) {
				param = pair[1];
			}
		}

		return param;
	}
}

EventEmitter(Previewer.prototype);

export default Previewer;
